/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.lock.service.memory;


import io.iec.edp.caf.commons.exception.ExceptionLevel;

import io.iec.edp.caf.commons.entity.CAFContextInfo;
import io.iec.edp.caf.commons.spi.utils.CAFContextResolverUtils;
import io.iec.edp.caf.lock.service.api.api.*;
import io.iec.edp.caf.lockservice.api.LockStatus;

import io.iec.edp.caf.lock.service.api.exception.DLErrorDefinition;
import io.iec.edp.caf.lock.service.api.exception.DistributedLockException;

import io.iec.edp.caf.lock.service.api.utils.DataValidator;
import io.iec.edp.caf.lockservice.api.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;

import java.io.IOException;
import java.time.Duration;
import java.util.*;


/**
 * 内存锁服务实现类
 *
 * @author wangyandong
 */
@Slf4j
public class MemoryLockServiceImpl implements LockService {

    private DistributedLockFactory factory;
    private LockExtension lockExtension;

    public MemoryLockServiceImpl(DistributedLockFactory factory, LockExtension lockExtension) {
        this.factory = factory;
        this.lockExtension=lockExtension;
    }

    @Override
    public LockResult addLock(String mkId, String dataCat, String dataId, DataLockOptions option, String funcId, String comment, @Nullable DistributedLockOptions distributedLockOptions) {
        DataValidator.checkForEmptyString(mkId, "mkId");
        DataValidator.checkForEmptyString(dataCat, "dataCat");
        DataValidator.checkForEmptyString(dataId, "dataId");
        DataValidator.checkForNullReference(option, "options");
        LockResult result = this.internalAddLock(mkId, dataCat, dataId, option, funcId, comment, null, distributedLockOptions);

        return result;
    }

    /**
     * @param mkId    模块ID
     * @param dataCat 数据种类
     * @param dataId  数据ID
     * @param option  数据锁的设置选项
     * @param funcId  加锁的功能Id
     * @param comment 备注
     * @return
     */
    @Override
    public LockResult addLock(String mkId, String dataCat, String dataId, DataLockOptions option, String funcId, String comment) {
        return this.addLock(mkId, dataCat, dataId, option, funcId, comment, null);
    }

    @Override
    @Deprecated
    public BatchLockResult addBatchLock(String mkId, String dataCat, List<String> dataIds, String groupId, DataLockOptions option, String funcId, String comment, @Nullable DistributedLockOptions distributedLockOptions) {
        return this.addBatchLock(mkId, dataCat, dataIds, groupId, option.getPersistenceTime(), funcId, comment);
    }

    @Override
    @Deprecated
    public BatchLockResult addBatchLock(String mkId, String dataCat, List<String> dataIds, String groupId, DataLockOptions option, String funcId, String comment) {
        return this.addBatchLock(mkId, dataCat, dataIds, groupId, option, funcId, comment, null);
    }

    /**
     * 批量锁加锁方法
     *
     * @param mkId            模块ID
     * @param dataCat         数据种类
     * @param dataIds         数据ID列表
     * @param groupId         批量锁标识，可空
     * @param persistenceTime 持续时间
     * @param funcId          功能ID
     * @param comment         备注
     * @return
     */
    @Override
    public BatchLockResult addBatchLock(String mkId, String dataCat, List<String> dataIds, @Nullable String groupId, Duration persistenceTime, String funcId, String comment) {
        DataValidator.checkForEmptyString(mkId, "mkId");
        DataValidator.checkForEmptyString(dataCat, "dataCat");
        DataValidator.checkForNullReference(dataIds, "dataIDs");
        DataValidator.checkForNullReference(persistenceTime, "persistenceTime");

//        //如果不为实例级锁，首先检查session是否在线，不在线抛异常
//        if (this.isSessionNotValid(CAFContext.current.getSessionId()))
//            throw new DistributedLockException("pfcomm", DLErrorDefinition.AddLock_IsSession_Error,
//                    "Session:" + CAFContext.current.getSessionId() + " is not online!", null, ExceptionLevel.Error, false);

        if (groupId == null || "".equals(groupId))
            groupId = UUID.randomUUID().toString();

        BatchLockResult result = new BatchLockResult();
        // 获取持久资源
        String resource = String.format("%s-%s", mkId, dataCat);
        result.setGroupId(groupId);
        DistributedLock distributedLock=null;
        try{
            distributedLock= this.factory.createLock(resource, Duration.ofSeconds(20));
            //若得不到分布式锁等待
            if (distributedLock == null || !distributedLock.isAcquired()) {
                result.setLockedEntities(null);
                result.setSuccess(false);
                return result;
            }
            result=lockExtension.addBatchLock(mkId,dataCat,dataIds,groupId,persistenceTime,funcId,comment);
            return result;
        } catch (Throwable e) {
            throw new io.iec.edp.caf.lock.service.api.exception.DistributedLockException("caf", DLErrorDefinition.AddBatchLock_Error,"addBatchLockError",e, ExceptionLevel.Error,false);
        }finally {
            try {
                if (distributedLock != null)
                    distributedLock.close();
            } catch (Exception e) {
                throw new io.iec.edp.caf.lock.service.api.exception.DistributedLockException("caf", DLErrorDefinition.AddBatchLock_Error,"closeBatchLockError",e, ExceptionLevel.Error,false);
            }
        }
    }




//    private int deleteBatchLockBySession(List<BatchLockEntity> entities,ContextInfo contextInfo) {
//        int count = 0;
//        String sessionId="";
//        if(contextInfo!=null){
//            sessionId=contextInfo.getSessionId();
//        }
//        for (BatchLockEntity entity : entities) {
//            if (entity.getSessionId() != null && !"".equals(entity.getSessionId()) && sessionId != null && !"".equals(sessionId)) {
//                if (!entity.getSessionId().equals(sessionId) && this.isSessionNotValid(entity.getSessionId())) {
//                    this.batchRepo.deleteById(entity.getId());
//                    count += 1;
//                }
//            }
//        }
//        return count;
//    }


    @Override
    @Deprecated
    public boolean removeLock(String lockId, boolean removeSharedLock, String sessionId) {
        DataValidator.checkForEmptyString(lockId, "lockId");
        return this.removeLock(lockId);
    }

    /**
     * 根据锁编号解锁
     *
     * @param lockId 锁编号
     * @return 是否成功的标识
     */
    public boolean removeLock(String lockId) {
        return lockExtension.removeLock(lockId);
    }

    @Override
    public boolean removeLock(RemoveLockInstance removeLockInstance){
        return lockExtension.removeLock(removeLockInstance);
    }

    @Override
    public boolean removeSessionLock(String sessionId, int tenantId) {
        return lockExtension.removeSessionLock(sessionId,tenantId);
    }

    @Override
    public boolean removeBatchLock(String groupId) {
        return lockExtension.removeBatchLock(groupId);
    }

    @Override
    public boolean removeInvalidLocks(int tenantId) {
        // to do
        return true;
    }

    @Override
    public  boolean removeBatchLockByContext(String contextId){
        return lockExtension.removeBatchLockByContext(contextId);
    }


    /**
     * 内部执行加锁逻辑
     */
    private LockResult internalAddLock(String mkId, String dataCat, String dataId,
                                       DataLockOptions option, String funcId, String comment, String groupId, @Nullable DistributedLockOptions distributedLockOptions) {
        LockResult result = new LockResult();
        //结果默认为不成功
        result.setSuccess(false);
        //根据上下文获取租户ID
        CAFContextInfo contextInfo= CAFContextResolverUtils.getCAFContextInfo();
        int tenantId =0;// this.contextService.getTenantId();
        if(contextInfo!=null){
            tenantId=contextInfo.getTenantId();
        }
        DistributedLock distributedLock = null;
        //根据资源、租户等信息构造锁编号
        // 获取持久资源
        String resource = String.format("%s-%s-%s-%s", tenantId, mkId, dataCat, dataId);

        //如果不为实例级锁，首先检查session是否在线，不在线抛异常
//        if (this.isSessionNotValid(CAFContext.current.getSessionId())) {
//            throw new DistributedLockException("pfcomm", DLErrorDefinition.AddLock_IsSession_Error,
//                    "Session:" + CAFContext.current.getSessionId() + " is not online!", null, ExceptionLevel.Error, false);
//        }

        Duration expiryTime = distributedLockOptions == null ? Duration.ofSeconds(20) : distributedLockOptions.getExpiryTime();
        try {

            distributedLock = this.factory.createLock(resource, expiryTime);
            //若得不到分布式锁则直接返回
            if (distributedLock == null || !distributedLock.isAcquired())
                throw new DistributedLockException("pfcomm", DLErrorDefinition.AddLock_IsAcquired_Error,
                        "DistributedLock get nothing!", null, ExceptionLevel.Error, false);
            result=lockExtension.addLock(mkId,dataCat,dataId,option,funcId,comment);
            return result;
        } catch (Throwable e) {
            throw new io.iec.edp.caf.lock.service.api.exception.DistributedLockException("caf", DLErrorDefinition.AddLock_GetLock_Error,"addLockError",e, ExceptionLevel.Error,false);
        } finally {
            try {
                if (distributedLock != null)
                    distributedLock.close();
            } catch (IOException e) {
                throw new io.iec.edp.caf.lock.service.api.exception.DistributedLockException("caf", DLErrorDefinition.AddLock_GetLock_Error,"closeLockError",e, ExceptionLevel.Error,false);
            }
        }
    }


    public LockStatus getLockStatus (LockEntity lockEntity) {
//        return  checkValidate(lockEntity);
        return lockExtension.getLockStatus(lockEntity);
    }

    /**
     * 根据gropuId获取dataIds
     * @param groupId
     * @return
     */
    @Override
    public List<String>  getDataIds(String groupId){
        return lockExtension.getDataIds(groupId);
    }

}
